# Plugin Hooks

This chapter introduces the plugin hooks available for Rsbuild plugins.

## Overview

### Common Hooks

- [modifyRsbuildConfig](#modifyrsbuildconfig): Modify the configuration passed to Rsbuild.
- [modifyEnvironmentConfig](#modifyenvironmentconfig): Modify the Rsbuild configuration of a specific environment.
- [modifyRspackConfig](#modifyrspackconfig): Modify the configuration passed to Rspack.
- [modifyBundlerChain](#modifybundlerchain): Modify the configuration of Rspack through the chain API.
- [modifyHTMLTags](#modifyhtmltags): Modify the tags that are injected into the HTML.
- [onBeforeCreateCompiler](#onbeforecreatecompiler): Called before creating a compiler instance.
- [onAfterCreateCompiler](#onaftercreatecompiler): Called after creating a compiler instance and before building.
- [onBeforeEnvironmentCompile](#onbeforeenvironmentcompile): Called before the compilation of a single environment.
- [onAfterEnvironmentCompile](#onafterenvironmentcompile): Called after the compilation of a single environment. You can get the build result information.
- [onExit](#onexit): Called when the process is about to exit.

### Dev Hooks

Called only when running the `rsbuild dev` command or the `rsbuild.startDevServer()` method.

- [onBeforeStartDevServer](#onbeforestartdevserver): Called before starting the dev server.
- [onAfterStartDevServer](#onafterstartdevserver): Called after starting the dev server.
- [onDevCompileDone](#ondevcompiledone): Called after each build in development mode.

Called only when Rsbuild is restarted or when the `close()` method of `rsbuild.build()` is executed.

- [onCloseDevServer](#onclosedevserver): Called when the dev server is closed.

### Build Hooks

Called only when running the `rsbuild build` command or the `rsbuild.build()` method.

- [onBeforeBuild](#onbeforebuild): Called before running the production build.
- [onAfterBuild](#onafterbuild): Called after running the production build. You can get the build result information.

Called only when Rsbuild is restarted or when the `close()` method of `rsbuild.build()` is executed.

- [onCloseBuild](#onclosebuild): Called when the build is closed.

### Preview Hooks

Called only when running the `rsbuild preview` command or the `rsbuild.preview()` method.

- [onBeforeStartProdServer](#onbeforestartprodserver): Called before starting the production server.
- [onAfterStartProdServer](#onafterstartprodserver): Called after starting the production server.

## Hooks Order

### Dev Hooks

When the `rsbuild dev` command or `rsbuild.startDevServer()` method is executed, Rsbuild will execute the following hooks in order:

- [modifyRsbuildConfig](#modifyrsbuildconfig)
- [modifyEnvironmentConfig](#modifyenvironmentconfig)
- [onBeforeStartDevServer](#onbeforestartdevserver)
- [modifyBundlerChain](#modifybundlerchain)
- [modifyRspackConfig](#modifyrspackconfig)
- [onBeforeCreateCompiler](#onbeforecreatecompiler)
- [onAfterCreateCompiler](#onaftercreatecompiler)
- [onBeforeEnvironmentCompile](#onbeforeenvironmentcompile)
- [onAfterStartDevServer](#onafterstartdevserver)
- [modifyHTMLTags](#modifyhtmltags)
- [onAfterEnvironmentCompile](#onafterenvironmentcompile)
- [onDevCompileDone](#ondevcompiledone)
- [onCloseDevServer](#onclosedevserver)
- [onExit](#onexit)

When rebuilding, the following hooks will be triggered again:

- [onBeforeEnvironmentCompile](#onbeforeenvironmentcompile)
- [modifyHTMLTags](#modifyhtmltags)
- [onAfterEnvironmentCompile](#onafterenvironmentcompile)
- [onDevCompileDone](#ondevcompiledone)

### Build Hooks

When the `rsbuild build` command or `rsbuild.build()` method is executed, Rsbuild will execute the following hooks in order:

- [modifyRsbuildConfig](#modifyrsbuildconfig)
- [modifyEnvironmentConfig](#modifyenvironmentconfig)
- [modifyBundlerChain](#modifybundlerchain)
- [modifyRspackConfig](#modifyrspackconfig)
- [onBeforeCreateCompiler](#onbeforecreatecompiler)
- [onAfterCreateCompiler](#onaftercreatecompiler)
- [onBeforeBuild](#onbeforebuild)
- [onBeforeEnvironmentCompile](#onbeforeenvironmentcompile)
- [modifyHTMLTags](#modifyhtmltags)
- [onAfterEnvironmentCompile](#onafterenvironmentcompile)
- [onAfterBuild](#onafterbuild)
- [onCloseBuild](#onclosebuild)
- [onExit](#onexit)

When rebuilding, the following hooks will be triggered again:

- [onBeforeBuild](#onbeforebuild)
- [onBeforeEnvironmentCompile](#onbeforeenvironmentcompile)
- [modifyHTMLTags](#modifyhtmltags)
- [onAfterEnvironmentCompile](#onafterenvironmentcompile)
- [onAfterBuild](#onafterbuild)

### Preview Hooks

When executing the `rsbuild preview` command or `rsbuild.preview()` method, Rsbuild will execute the following hooks in order:

- [modifyRsbuildConfig](#modifyrsbuildconfig)
- [modifyEnvironmentConfig](#modifyenvironmentconfig)
- [onBeforeStartProdServer](#onbeforestartprodserver)
- [onAfterStartProdServer](#onafterstartprodserver)
- [onExit](#onexit)

## Global Hooks vs Environment Hooks

In Rsbuild, some of the plugin hooks are global hooks. The execution of these hooks is often related to Rsbuild's own startup process or global logic and is shared under all environments. Such as:

- `modifyRsbuildConfig` is used to modify the basic configuration of Rsbuild. The basic configuration will eventually be merged with the environment configuration;
- `onBeforeStartDevServer` and `onAfterStartDevServer` are related to the Rsbuild dev server startup process, all environments share Rsbuild's dev server, middlewares, and Web Socket.

Correspondingly, there are some plugin hooks that are related to the current environment. These hooks are executed with a specific environment context and are triggered multiple times depending on the environment.

### Global Hooks

- [modifyRsbuildConfig](#modifyrsbuildconfig)
- [onBeforeStartDevServer](#onbeforestartdevserver)
- [onBeforeCreateCompiler](#onbeforecreatecompiler)
- [onAfterCreateCompiler](#onaftercreatecompiler)
- [onAfterStartDevServer](#onafterstartdevserver)
- [onDevCompileDone](#ondevcompiledone)
- [onCloseDevServer](#onclosedevserver)
- [onBeforeBuild](#onbeforebuild)
- [onAfterBuild](#onafterbuild)
- [onCloseBuild](#onclosebuild)
- [onBeforeStartProdServer](#onbeforestartprodserver)
- [onAfterStartProdServer](#onafterstartprodserver)
- [onExit](#onexit)

### Environment Hooks

- [modifyEnvironmentConfig](#modifyenvironmentconfig)
- [modifyBundlerChain](#modifybundlerchain)
- [modifyRspackConfig](#modifyrspackconfig)
- [modifyHTMLTags](#modifyhtmltags)
- [onBeforeEnvironmentCompile](#onbeforeenvironmentcompile)
- [onAfterEnvironmentCompile](#onafterenvironmentcompile)

## Callback Order

### Default Behavior

If multiple plugins register the same hook, the callback functions of the hook will execute in the order in which they were registered.

In the following example, the console will output `'1'` and `'2'` in sequence:

```ts
const plugin1 = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig(() => console.log('1'));
  },
});

const plugin2 = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig(() => console.log('2'));
  },
});

rsbuild.addPlugins([plugin1, plugin2]);
```

### `order` Field

When registering a hook, you can declare the order of hook through the `order` field.

```ts
type HookDescriptor<T extends (...args: any[]) => any> = {
  handler: T;
  order: 'pre' | 'post' | 'default';
};
```

In the following example, the console will sequentially output `'2'` and `'1'`, because `order` was set to `pre` when plugin2 called `modifyRsbuildConfig`.

```ts
const plugin1 = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig(() => console.log('1'));
  },
});

const plugin2 = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig({
      handler: () => console.log('2'),
      order: 'pre',
    });
  },
});

rsbuild.addPlugins([plugin1, plugin2]);
```

## Common Hooks

### modifyRsbuildConfig

Modify the config passed to the Rsbuild, you can directly modify the config object, or return a new object to replace the previous object.

:::warning
`modifyRsbuildConfig` is a global hook. To add support for your plugin as an [environment-specific plugin](/guide/advanced/environments#add-plugins-for-specified-environment), you should use [modifyEnvironmentConfig](/plugins/dev/hooks#modifyenvironmentconfig) instead of `modifyRsbuildConfig`.
:::

- **Type:**

```ts
type ModifyRsbuildConfigUtils = {
  mergeRsbuildConfig: typeof mergeRsbuildConfig;
};

function ModifyRsbuildConfig(
  callback: (
    config: RsbuildConfig,
    utils: ModifyRsbuildConfigUtils,
  ) => MaybePromise<RsbuildConfig | void>,
): void;
```

- **Example:** Setting a default value for a specific config option:

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig((config) => {
      config.html ||= {};
      config.html.title = 'My Default Title';
    });
  },
});
```

- **Example:** Using `mergeRsbuildConfig` to merge config objects, and return the merged object.

```ts
import type { RsbuildConfig } from '@rsbuild/core';

const myPlugin = () => ({
  setup: (api) => {
    api.modifyRsbuildConfig((userConfig, { mergeRsbuildConfig }) => {
      const extraConfig: RsbuildConfig = {
        source: {
          // ...
        },
        output: {
          // ...
        },
      };

      // extraConfig will override fields in userConfig,
      // If you do not want to override the fields in userConfig,
      // you can adjust to `mergeRsbuildConfig(extraConfig, userConfig)`
      return mergeRsbuildConfig(userConfig, extraConfig);
    });
  },
});
```

:::tip
`modifyRsbuildConfig` cannot be used to register additional Rsbuild plugins. This is because at the time `modifyRsbuildConfig` is executed, Rsbuild has already initialized all plugins and started executing the callbacks of the hooks.

For details, please refer to [Plugin registration phase](/config/plugins#plugin-registration-phase).
:::

### modifyEnvironmentConfig

Modify the Rsbuild configuration of a specific environment.

In the callback function, the config object in the parameters has already been merged with the common Rsbuild configuration. You can directly modify this config object, or you can return a new object to replace it.

- **Type:**

```ts
type ArrayAtLeastOne<A, B> = [A, ...Array<A | B>] | [...Array<A | B>, A];

type ModifyEnvironmentConfigUtils = {
  /** Current environment name */
  name: string;
  mergeEnvironmentConfig: (
    ...configs: ArrayAtLeastOne<MergedEnvironmentConfig, EnvironmentConfig>
  ) => EnvironmentConfig;
};

function ModifyEnvironmentConfig(
  callback: (
    config: EnvironmentConfig,
    utils: ModifyEnvironmentConfigUtils,
  ) => MaybePromise<EnvironmentConfig | void>,
): void;
```

- **Example:** Set a default value for the Rsbuild config of a specified environment:

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.modifyEnvironmentConfig((config, { name }) => {
      if (name !== 'web') {
        return config;
      }
      config.html.title = 'My Default Title';
    });
  },
});
```

- **Example:** Using `mergeEnvironmentConfig` to merge config objects, and return the merged object.

```ts
import type { EnvironmentConfig } from '@rsbuild/core';

const myPlugin = () => ({
  setup: (api) => {
    api.modifyEnvironmentConfig((userConfig, { mergeEnvironmentConfig }) => {
      const extraConfig: EnvironmentConfig = {
        source: {
          // ...
        },
        output: {
          // ...
        },
      };

      // extraConfig will override fields in userConfig,
      // If you do not want to override the fields in userConfig,
      // you can adjust to `mergeEnvironmentConfig(extraConfig, userConfig)`
      return mergeEnvironmentConfig(userConfig, extraConfig);
    });
  },
});
```

### modifyRspackConfig

To modify the Rspack config, you can directly modify the config object, or return a new object to replace the previous object.

:::tip
`modifyRspackConfig` is executed earlier than [tools.rspack](/config/tools/rspack). Therefore, the modifications made by `tools.rspack` cannot be obtained in `modifyRspackConfig`.
:::

- **Type:**

```ts
type ModifyRspackConfigUtils = {
  environment: EnvironmentContext;
  env: string;
  isDev: boolean;
  isProd: boolean;
  target: RsbuildTarget;
  isServer: boolean;
  isWebWorker: boolean;
  rspack: Rspack;
};

function ModifyRspackConfig(
  callback: (
    config: Rspack.Configuration,
    utils: ModifyRspackConfigUtils,
  ) => Promise<RspackConfig | void> | Rspack.Configuration | void,
): void;
```

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.modifyRspackConfig((config, utils) => {
      if (utils.env === 'development') {
        config.devtool = 'eval-cheap-source-map';
      }
    });
  },
});
```

### modifyBundlerChain

import RspackChain from '@en/shared/rspackChain.mdx';

<RspackChain />

`modifyBundlerChain` is used to call `rspack-chain` to modify the Rspack configuration. Its usage is the same as [tools.bundlerChain](/config/tools/bundler-chain).

- **Type:**

```ts
type ModifyBundlerChainUtils = {
  environment: EnvironmentContext;
  env: string;
  isDev: boolean;
  isProd: boolean;
  target: RsbuildTarget;
  isServer: boolean;
  isWebWorker: boolean;
  CHAIN_ID: ChainIdentifier;
  HtmlPlugin: typeof import('html-rspack-plugin');
  bundler: {
    BannerPlugin: rspack.BannerPlugin;
    DefinePlugin: rspack.DefinePlugin;
    IgnorePlugin: rspack.IgnorePlugin;
    ProvidePlugin: rspack.ProvidePlugin;
    HotModuleReplacementPlugin: rspack.HotModuleReplacementPlugin;
  };
};

function ModifyBundlerChain(
  callback: (
    chain: RspackChain,
    utils: ModifyBundlerChainUtils,
  ) => Promise<void> | void,
): void;
```

- **Example:**

```ts
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';

const myPlugin = () => ({
  setup: (api) => {
    api.modifyBundlerChain((chain, utils) => {
      if (utils.env === 'development') {
        chain.devtool('eval');
      }

      chain.plugin('bundle-analyze').use(BundleAnalyzerPlugin);
    });
  },
});
```

### modifyHTMLTags

Modify the tags that are injected into the HTML.

- **Type:**

```ts
type HtmlBasicTag = {
  // Tag name
  tag: string;
  // Attributes of the tag
  attrs?: Record<string, string | boolean | null | undefined>;
  // innerHTML of the tag
  children?: string;
};

type HTMLTags = {
  // Tags group inserted into <head>
  headTags: HtmlBasicTag[];
  // Tags group inserted into <body>
  bodyTags: HtmlBasicTag[];
};

type Context = {
  /**
   * The Compiler object of Rspack.
   */
  compiler: Rspack.Compiler;
  /**
   * The Compilation object of Rspack.
   */
  compilation: Rspack.Compilation;
  /**
   * URL prefix of assets.
   * @example 'https://example.com/'
   */
  assetPrefix: string;
  /**
   * The name of the HTML file, relative to the dist directory.
   * @example 'index.html'
   */
  filename: string;
  /**
   * The environment context for current build.
   */
  environment: EnvironmentContext;
};

function ModifyHTMLTags(
  callback: (tags: HTMLTags, context: Context) => MaybePromise<HTMLTags>,
): void;
```

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.modifyHTMLTags(({ headTags, bodyTags }) => {
      headTags.push({
        tag: 'script',
        attrs: { src: 'https://example.com/foo.js' },
      });
      bodyTags.push({
        tag: 'script',
        children: 'console.log("hello world!");',
      });

      return { headTags, bodyTags };
    });
  },
});
```

### onBeforeCreateCompiler

import OnBeforeCreateCompiler from '@en/shared/onBeforeCreateCompiler.mdx';

<OnBeforeCreateCompiler />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onBeforeCreateCompiler(({ bundlerConfigs }) => {
      console.log('the bundler config is ', bundlerConfigs);
    });
  },
});
```

### onAfterCreateCompiler

import OnAfterCreateCompiler from '@en/shared/onAfterCreateCompiler.mdx';

<OnAfterCreateCompiler />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onAfterCreateCompiler(({ compiler }) => {
      console.log('the compiler is ', compiler);
    });
  },
});
```

### onBeforeEnvironmentCompile

import OnBeforeEnvironmentCompile from '@en/shared/onBeforeEnvironmentCompile.mdx';

<OnBeforeEnvironmentCompile />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onBeforeEnvironmentCompile(({ bundlerConfig, environment }) => {
      console.log(
        `the bundler config for the ${environment.name} is `,
        bundlerConfig,
      );
    });
  },
});
```

### onAfterEnvironmentCompile

import OnAfterEnvironmentCompile from '@en/shared/onAfterEnvironmentCompile.mdx';

<OnAfterEnvironmentCompile />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onAfterEnvironmentCompile(({ isFirstCompile, stats }) => {
      console.log(stats?.toJson(), isFirstCompile);
    });
  },
});
```

## Build Hooks

### onBeforeBuild

import OnBeforeBuild from '@en/shared/onBeforeBuild.mdx';

<OnBeforeBuild />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onBeforeBuild(({ bundlerConfigs }) => {
      console.log('the bundler config is ', bundlerConfigs);
    });
  },
});
```

### onAfterBuild

import OnAfterBuild from '@en/shared/onAfterBuild.mdx';

<OnAfterBuild />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onAfterBuild(({ isFirstCompile, stats }) => {
      console.log(stats?.toJson(), isFirstCompile);
    });
  },
});
```

### onCloseBuild

import OnCloseBuild from '@en/shared/onCloseBuild.mdx';

<OnCloseBuild />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onCloseBuild(() => {
      console.log('close build!');
    });
  },
});
```

## Dev Hooks

### onBeforeStartDevServer

import OnBeforeStartDevServer from '@en/shared/onBeforeStartDevServer.mdx';

<OnBeforeStartDevServer />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onBeforeStartDevServer(() => {
      console.log('before start!');
    });
  },
});
```

### onAfterStartDevServer

import OnAfterStartDevServer from '@en/shared/onAfterStartDevServer.mdx';

<OnAfterStartDevServer />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onAfterStartDevServer(({ port, routes }) => {
      console.log('this port is: ', port);
      console.log('this routes is: ', routes);
    });
  },
});
```

### onAfterEnvironmentCompile

import OnDevCompileEnvironmentDone from '@en/shared/onAfterEnvironmentCompile.mdx';

<OnDevCompileEnvironmentDone />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onAfterEnvironmentCompile(({ isFirstCompile }) => {
      if (isFirstCompile) {
        console.log('first compile!');
      } else {
        console.log('re-compile!');
      }
    });
  },
});
```

### onDevCompileDone

import OnDevCompileDone from '@en/shared/onDevCompileDone.mdx';

<OnDevCompileDone />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onDevCompileDone(({ isFirstCompile }) => {
      if (isFirstCompile) {
        console.log('first compile!');
      } else {
        console.log('re-compile!');
      }
    });
  },
});
```

### onCloseDevServer

import OnCloseDevServer from '@en/shared/onCloseDevServer.mdx';

<OnCloseDevServer />

- **Example:**

```ts
rsbuild.onCloseDevServer(async () => {
  console.log('close dev server!');
});
```

## Preview Hooks

### onBeforeStartProdServer

import OnBeforeStartProdServer from '@en/shared/onBeforeStartProdServer.mdx';

<OnBeforeStartProdServer />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onBeforeStartProdServer(() => {
      console.log('before start!');
    });
  },
});
```

### onAfterStartProdServer

import OnAfterStartProdServer from '@en/shared/onAfterStartProdServer.mdx';

<OnAfterStartProdServer />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onAfterStartProdServer(({ port, routes }) => {
      console.log('this port is: ', port);
      console.log('this routes is: ', routes);
    });
  },
});
```

## Other Hooks

### onExit

import OnExit from '@en/shared/onExit.mdx';

<OnExit />

- **Example:**

```ts
const myPlugin = () => ({
  setup: (api) => {
    api.onExit(() => {
      console.log('exit!');
    });
  },
});
```
